#!/usr/bin/env python
# -*- coding: UTF8 -*-

# qemu_manager.py
#
#  Copyright (c) 2006 Marcelo Lira dos Santos
#
#  Authors: Marcelo Lira dos Santos <setanta@gmail.com>
#           Lauro Moura <lauromoura@gmail.com>
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License as
#  published by the Free Software Foundation; either version 2 of the
#  License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
#  USA

import os
import signal
import gobject
from socket import *
from subprocess import Popen

BASE_PORT = 4444
LOCALHOST = '127.0.0.1'

class QemuManager(gobject.GObject):

    __gproperties__ = {
        'serial_port' : (int, 'serial TCP port',
                         'TCP port to connect with Qemu serial port',
                         1025, 65550, BASE_PORT,
                         gobject.PARAM_READWRITE),
        'parallel_port' : (int, 'serial TCP port',
                           'TCP port to connect with Qemu parallel port',
                           1025, 65550, BASE_PORT + 1,
                           gobject.PARAM_READWRITE),
        'qemu_binary' : (str, 'qemu binary file', 'path to qemu binary file',
                         None, gobject.PARAM_READWRITE),
        'bios_dir' : (str, 'bios files folder', 'path to bios files folder',
                      None, gobject.PARAM_READWRITE),
        'image_file' : (str, 'vm image file', 'path to virtual machine file',
                        None, gobject.PARAM_READWRITE),
    }

    __gsignals__ = {
        'data-received' : (gobject.SIGNAL_RUN_LAST, None, (int, int)),
        'connection-closed' : (gobject.SIGNAL_RUN_LAST, None, (int, ))
    }

    (
        SERIAL,
        PARALLEL
    ) = range(2)

    def __init__(self):
        gobject.GObject.__init__(self)
        self.properties = {'serial-port'   : BASE_PORT,
                           'parallel-port' : BASE_PORT + 1,
                           'qemu-binary'   : None,
                           'bios-dir'      : None,
                           'image-file'    : None }

        self.instance = None
        self.connections = [None, None]
        self.conn_src_id = [None, None]

        self.port_names = [None, None]
        self.port_names[QemuManager.SERIAL] = 'serial-port'
        self.port_names[QemuManager.PARALLEL] = 'parallel-port'

    '''def __str__(self):
        return 'qemu-binary: %s\nbios-dir: %s\nimage-file: %s\n' \
               'serial-port: %d\nparallel-port: %d\n' % \
               (self.properties['qemu-binary'],
                self.properties['bios-dir'],
                self.properties['image-file'],
                self.properties['serial-port'],
                self.properties['parallel-port'])
    '''
    def do_get_property(self, prop):
        try:
            return self.properties[prop.name]
        except:
            raise AttributeError, 'unknown property %s' % prop.name

    def do_set_property(self, prop, value):
        try:
            self.properties[prop.name] = value
        except:
            raise AttributeError, 'unknown property %s' % prop.name

    def is_up(self, port):
        try:
            up = self.connections[port] is not None
        except:
            up = False

        return up

    def init_connection(self, port):
        try:
            port_name = self.port_names[port]
            host_port = (LOCALHOST, self.properties[port_name])
            self.connections[port] = socket(AF_INET, SOCK_STREAM)
            self.connections[port].connect(host_port)
            self.conn_src_id[port] = \
                gobject.io_add_watch(self.connections[port],
                                     gobject.IO_IN | gobject.IO_HUP,
                                     self.recv, port)
        except:
            raise

    def close_connection(self, port):
        try:
            self.connections[port].close()
        except:
            raise

    def send(self, port, value):
        try:
            self.connections[port].send(value)
        except:
            raise

    def recv(self, source, cb_condition, port):
        if self.instance.poll() == 0 or \
           cb_condition & gobject.IO_HUP:
            self.connections[port].close()
            self.emit('connection-closed', port)
            return False

        elif cb_condition & gobject.IO_IN:
            c = self.connections[port].recv(1)

            if c:
                self.emit('data-received', port, ord(c))

        return True

    def start(self):
        try:
            self.instance = \
                Popen([self.properties['qemu-binary'],
                       '-L', self.properties['bios-dir'],
                       '-no-kqemu', '-localtime',
                       '-serial', 'tcp::%d,server,nowait' % \
                       self.properties['serial-port'],
                       '-parallel', 'tcp::%d,server,nowait' % \
                       self.properties['parallel-port'],
                       self.properties['image-file']])
            ok = True
        except:
            ok = False

        return ok

    def stop(self):
        if self.instance is not None and self.instance.poll() != 0:
            os.kill(self.instance.pid, signal.SIGKILL)
            self.instance = None
            for port in (QemuManager.SERIAL, QemuManager.PARALLEL):
                try:
                    if self.is_up(port):
                        self.close_connection(port)
                except:
                    pass

